import collections

from utils import attributeMaker, AttributeHolder, OrderedDictWithLocal

from wineObjects import colors
import difflib

# the function creating a new cepages store (here a list of list)
_newcepages = lambda  : [ list() for c in colors ]


@attributeMaker('cepages', _newcepages)
class CepagesAtt(AttributeHolder):
    """AttributeHolder dedicated to cepages """
    inherited = True # this attributes is inherited by children nodes

    def cepages(self, color):
        cepagesU = self._get_user_cepages()
        cepagesS = self._get_system_cepages()
        cep = []+ cepagesU[color] if cepagesU else []
        cep += cepagesS[color] if cepagesS else []
        return cep

    def hasCepage(self, color, cep):
        cepagL = self._get_system_cepages()
        l = difflib.get_close_matches(cep, cepagL[color], 1, cutoff=0.6)
        if l != []: return l[0]
        cepagL = self._get_user_cepages()
        if cepagL:
            l = difflib.get_close_matches(cep, cepagL[color], 1, cutoff=0.6)
            if l != []: return l[0]
        return ""

    def addCepages(self,color,c, user=True):
        cep = self.hasCepage(color,c)
        if  cep == "":
            self._build_cepages(user)
            self._user_cepages[color].append(c)
        

    def compare(self, o, d):
        #print "--"*self.level, ' cepages ',self.name, '=',self.cepages(0) == o.cepages(0)
        ## if not self.cepages(0) == o.cepages(0):
        ##     print "--"*self.level,'   ~    ',self.cepages(0), o.cepages(0)
        ok = True
        for i in (0,1,2):
            if self.cepages(i) != o.cepages(i):
                d[self].append( 'cepages' )
                ok= False                
        return ok

    def toDict(self,user=False):
        d ={}
        cepages = self._user_cepages if user else self._system_cepages
        if cepages :
            d['cepages'] = cepages
        return d
    
    def fromDict(self,d,user=False):
        cepages = d.get('cepages',None)
        #if 'Lang' in self.name : print '  __ ', self.name, ' --> ', cepages
        if user:
            self._user_cepages = cepages
        else :
            self._system_cepages = cepages

    def _transferUDataToSData(self , sa):
        if self._user_cepages is None :
            return
        slist = sa._system_cepages
        if slist is None : slist = []
        for (i, ulist) in enumerate(self._user_cepages):
            for c in ulist:
                if c not in slist:
                    scep=sa._build_cepages(True)
                    scep[i].append( c )
        
    def synchronize(self, area):
        if self._user_cepages or area._user_cepages:
            area._build_cepages(True)
            self._build_cepages(True)
            #print self.name , len(self._user_cepages), len(area._user_cepages), self._user_cepages
            for i in range(len(colors)):
                cepage = set(area._user_cepages[i])
                cepage.update( set(self._user_cepages[i]) )
                area._user_cepages[i] = list( cepage )
                self._user_cepages[i] = list( cepage )                



_newsubareas = lambda : OrderedDictWithLocal()
@attributeMaker('subAreas',_newsubareas)
class SubAreaAtt(AttributeHolder):
    inherited = False

    sorted = False

    # a function expected to request a confirmation from users.
    #  it is expected the gui system provides a new one.
    confirmUDtransferToSD = lambda self, ua, sa : False 

    def iter_subAreas(self, reccursive=False):
        if self._system_subAreas:
            for a in self._system_subAreas.itervalues():
                yield a
                if reccursive:
                    for sa in a.iter_subAreas(reccursive=reccursive):
                        yield sa
        if self._user_subAreas:
            for a in self._user_subAreas.itervalues():
                yield a
                if reccursive:
                    for sa in a.iter_subAreas(reccursive=reccursive):
                        yield sa

        
    def subAreas(self):
        return list( self.iter_subAreas() )

    def iter_subAreasKeys(self):
        if self._system_subAreas:
            for a in self._system_subAreas.iterkeys():
                yield a
        if self._user_subAreas:
            for a in self._user_subAreas.iterkeys():
                yield a

    def subAreasKeys(self):
        return list(self.iter_subAreasKeys() )

    def subAreasItems(self):
        if self._system_subAreas:
            for u,v in self._system_subAreas.iteritems():
                yield u,v
        if self._user_subAreas:                
            for u,v in self._user_subAreas.iteritems():
                yield u,v
                
    def getSubArea(self, aname, fuzzy=False, checkUser=True):
        getF = OrderedDictWithLocal.getApproximate if fuzzy else OrderedDictWithLocal.get
        a = None
        if self._system_subAreas:
            a = getF(self._system_subAreas , aname , checkLocal= True)            
        if a is  None and self._user_subAreas and checkUser:
            a = getF(self._user_subAreas , aname , checkLocal= True)
        return a


    def addSubArea(self, area, addUserArea=True, **args):
        subAreas = self._build_subAreas(addUserArea)
        if isinstance(area,basestring):
            area = self.subAreaClass(area, parentArea=self, **args)
        #print ' ----  Area ', self.name, '  adding ',area , ' to user=',addUserArea, subAreas
        subAreas[area.name] = area
        area.parentArea = self
        if self._interActiveArea:
            setattr(self, area.name.replace(' ','_').replace('-','_'), area)
        return area


    def __getitem__(self,s):
        area = self.getSubArea(s, fuzzy = False)
        if area is None: # build it !
            print 'FFFF area __getitem__ ',[s ]
            area = self.addSubArea(s, True)
        return area

    
    def __setitem__(self,s, a):
        self._build_subAreas(user=True)
        a.parentArea  = self # ???
        self._user_subAreas[s] = a

    def compare(self, o, d= collections.defaultdict(list)):
        keys  = self.subAreasKeys()
        okeys = o.subAreasKeys()
        okeys.sort()
        keys.sort()
        keyC = (  keys == okeys )
        #print "--"*self.level,' area compare ',self.name, ' keys=',keys, 'okeys=',okeys
        if not keyC:
            #print "--"*self.level, ' areas comp ',self.name, ' !!! keys differs ',keys, okeys
            d[self].append('area keys')
            
        

        for (a1,a2) in zip( keys,okeys ) :
            r=self[a1].compare(o[a2],d)
            keyC = keyC and r
        return keyC 

    def checkUserContent(self):
        has = False
        if self._system_subAreas:
            for a in self._system_subAreas.values():
                if a.checkUserContent() :
                    has= True
        self.hasUserContent = has
        return has

    def toDict(self, user=False):
        d ={}
        subAreas = self._user_subAreas if user else self._system_subAreas

        # save sub areas in this area.
        if subAreas :
            for n,suba in subAreas.iteritems():
                d[n] = suba.toDict(user)

        # if saving user's content, check if system's subareas contain user content. if so, save it
        if user and self._system_subAreas:
            for a in self._system_subAreas.values():
                if a.hasUserContent :
                    d[a.name] = a.toDict(user)

        return {'areas' : d }


    def fromDict(self,d,user=False):
        """Fills the wine area hierarchy from the dictionnary d.
        Builds sub-areas when needed and call fromDict from these sub-areas"""
        d = d['areas']
        #subAreas = self._user_subAreas if user else self._system_subAreas
        #print '__'*self.level , self.name , user, ' -->', d.keys()
        #if user : print self._debugPrefix , self.name, ' areas are : ' , d.keys()
        for aname, v in d.iteritems():
            area = self.getSubArea(aname, fuzzy=False)
            #print '__'*(self.level+1), '  searched ',aname, ' got ',area, self, self.name , v
            if not area:
                area = self.addSubArea(aname, addUserArea=user, groupApp = 'isApp' not in v ) # last argument only usefull when filling region.
            area.fromDict(v,user)
            area._update_local_names()

            
    def _update_local_names(self):
        """fills the locale Dictionnary of sub-areas so searching by locale name is enabled"""
        if self._system_subAreas:
            localD = self._system_subAreas.localD            
            for a in self._system_subAreas.values():
                if a.localname:
                    localD[a.localname] = a

    def _updateUDataFromSData(self):
        if self._user_subAreas is not None and self._system_subAreas is not None :
            print ' updates in ',self.name

            systSubA = self._system_subAreas        
            toUpdate = []
            # loop over user's areas
            for a in self._user_subAreas.values():
                print ' --> testing area ',a.name
                # if it exists in the system areas
                s_a = self.getSubArea(a.name, checkUser=False, fuzzy=True) 
                if s_a is not None: # mark it to be updated
                    toUpdate.append( (a, s_a ) )

            for (ua , sa ) in toUpdate:
                if self.confirmUDtransferToSD(ua, sa):
                    ua._transferUDataToSData(sa)
                self._user_subAreas.pop(ua.name)
        # proceed to sub areas
        for a in self.subAreas():
            a._updateUDataFromSData()

    def _transferUDataToSData(self,  sa):
        """transfer the data in self into the system area sa.
        This is simply creating user sub areas in sa, then recopying self content into sa._user_subAreas
        (sub area transfer occurs through recursive calls of _updateUDataFromSData)
        """
        if self._user_subAreas is None:
            return
        #sa._build_cepages(True)
        sa._build_subAreas(True)
        for subA in self._user_subAreas.values():
            sa._user_subAreas[subA.name] = subA
        

    def synchronize(self, area):
        """This synchronizes the *user* sub-areas of self and area.
        System area are not synchronized.
        Missing sub-areas are just copied from one place to the other
        """
        selfSet = set( self._user_subAreas.keys()) if self._user_subAreas is not None else set()
        areaSet = set( area._user_subAreas.keys() ) if area._user_subAreas is not None else set()
        common = selfSet.intersection(areaSet)
        for a in common:
            self._user_subAreas[a].synchronize(area._user_subAreas[a])
        for a in selfSet-areaSet:
            area.addSubArea(a)
        for a in areaSet -selfSet:
            self.addSubArea(a)


    def sortSubAreas(self, compFunc = None):
        if self._system_subAreas is None or self.sorted:
            return
        
        sorted_dic = OrderedDictWithLocal()
        names = [ (a.localname, a) if a.localname else (k,a) for (k,a) in self._system_subAreas.iteritems() ]
        names.sort( cmp = compFunc)
        #print ' sorting in ',self.name, names
        for n,a in names:
            sorted_dic[a.name] = a
            a.sortSubAreas(compFunc = compFunc)
        self._system_subAreas = sorted_dic
        self.sorted = True
